// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "RunnableThread.h"
#include <map>
#include <vector>
#include <string>
#include <mutex>
#include <condition_variable>
#include <functional>
#include "ThreadCounter.h"

/**
 * Manages runnables and runnable threads.
 */
class ThreadManager
{
	friend class GameThread;
	friend class RenderThread;

	/** Critical section for ThreadList */
	std::mutex mThreadsCritical;
	/** Set if thread manager is initialized. */
	static bool mIsInitialized;
	/** List of thread objects to be ticked. */
	std::map<uint32, RunnableThread*> mThreads;
	/** List of task thread to be ticked */
	std::map<uint32, class TaskThread*> mTaskThreads;
	
	std::atomic_bool mTimeToExit;

	// Use to make sure RenderThread init run GameThread init
	HANDLE mWaitRenderInit;
	HANDLE mWaitGameInit;
	HANDLE mWaitRenderExit;
	HANDLE mWaitGameExit;

public:

	/**
	* Start All the thread
	*/
	void Start();

	/**
	* Stop All the thread
	*/
	void Stop();

	/**
	 * Use to make sure the Game and Render or more thread should exit
	 */
	bool IsTimeToExit() const;

	/**
	 * Queues the task for execution.
	 */
	void QueueTask(class BaseGraphTask* task, uint32 threadIdToExecuteOn);

	/**
	* Used internally to add a new thread object.
	*
	* @param Thread thread object.
	* @see RemoveThread
	*/
	void AddThread(uint32 threadId, RunnableThread* runnablethread);

	/**
	* Used internally to remove thread object.
	*
	* @param Thread thread object to be removed.
	* @see AddThread
	*/
	void RemoveThread(RunnableThread* runnablethread);


	/** Returns the name of a thread given its TLS id */
	const std::wstring& GetThreadName(uint32 threadId);

	/** Checks if thread manager has been initialized. Avoids creating the manager trough lazy initialization */
	static bool IsInitialized();

	/**
	 * Enumerate each thread.
	 *
	 */
	void ForEachThread(std::function<void(uint32, RunnableThread*)> func);

	/**
	 * Access to the singleton object.
	 *
	 * @return Thread manager object.
	 */
	static ThreadManager& Get();
};
